1 module hip.util.sumtype; 2 3 enum Type 4 { 5 undefined, 6 void_, 7 object, 8 string_, 9 bool_, 10 //Signed 11 byte_, 12 short_, 13 int_, 14 long_, 15 //Unsigned 16 ubyte_, 17 ushort_, 18 uint_, 19 ulong_, 20 //Floating 21 float_, 22 double_, 23 ///Untested 24 real_ 25 } 26 27 private union TypeUnion 28 { 29 string string_; 30 void* void_; 31 Object object; 32 bool bool_; 33 //Signed 34 byte byte_; 35 short short_; 36 int int_; 37 long long_; 38 //Unsigned 39 ubyte ubyte_; 40 ushort ushort_; 41 uint uint_; 42 ulong ulong_; 43 //Floating 44 float float_; 45 double double_; 46 ///Untested 47 real real_; 48 } 49 pure Type getType(T)() nothrow @nogc @safe 50 { 51 with(Type) 52 { 53 static if(is(T == string)) 54 return string_; 55 else static if(is(T == void*)) 56 return void_; 57 else static if(is(T : Object)) 58 return object; 59 else static if(is(T == bool)) 60 return bool_; 61 else static if(is(T == byte)) 62 return byte_; 63 else static if(is(T == short)) 64 return short_; 65 else static if(is(T == int)) 66 return int_; 67 else static if(is(T == long)) 68 return long_; 69 else static if(is(T == ubyte)) 70 return ubyte_; 71 else static if(is(T == ushort)) 72 return ushort_; 73 else static if(is(T == uint)) 74 return uint_; 75 else static if(is(T == ulong)) 76 return ulong_; 77 else static if(is(T == float)) 78 return float_; 79 else static if(is(T == double)) 80 return double_; 81 else static if(is(T == real)) 82 return real_; 83 else static assert(false, "Unimplemented for type "~T.stringof); 84 } 85 } 86 87 /** 88 * Use that when you want to hold arbitrary defined type (which usually must be converted) 89 * By using sumtype, your data will be converted only once and after that, it will be runtime 90 * type strict. 91 */ 92 struct Sumtype 93 { 94 Type type = Type.undefined; 95 TypeUnion data = void; 96 97 string getTypeName() const @nogc 98 { 99 switch(type) 100 { 101 static foreach(mem; __traits(allMembers, Type)) 102 { 103 case __traits(getMember, Type, mem): 104 return mem[0..$-1]; 105 } 106 default: 107 return "undefined"; 108 } 109 } 110 111 T get(T)() const 112 { 113 if(getType!T != type) 114 throw new Exception("Tried to get a mismatching type: "~T.stringof~" (expected "~getTypeName~")"); 115 return *cast(T*)(cast(void*)&data); 116 } 117 T set(T)(T value) 118 { 119 if(type == Type.undefined) 120 this.type = getType!T; 121 else if(type != getType!T) 122 throw new Exception("Tried to get a mismatching type: "~T.stringof~" (expected "~getTypeName~")"); 123 data = *cast(TypeUnion)(cast(void*)&data); 124 return value; 125 } 126 127 T opCast(T)() const 128 { 129 return get!T; 130 } 131 bool opBinary(string op : "in")(const Type t) const 132 { 133 return type == t; 134 } 135 alias opBinaryRight = opBinary; 136 137 static Sumtype make(string data) 138 { 139 return Sumtype(Type.string_, cast(TypeUnion)data); 140 } 141 142 static Sumtype make(T)(T data) if(!is(T == string)) 143 { 144 Sumtype s = void; 145 s.type = getType!T; 146 s.data = *cast(TypeUnion*)(cast(void*)&data); 147 return s; 148 } 149 150 static Sumtype make(T)(string data) 151 { 152 import hip.util.conv:to; 153 return make!T(to!T(data)); 154 } 155 }